﻿//------------------------------------------------------------------------------
// <copyright file="UserDefinedTypeHooks.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using Microsoft.Internal.GamesTest.Rpc.Server;

namespace Rpc.CSharpSample
{
    /// <summary>
    /// Implements hooks that deal with user-defined types.
    /// </summary>
    internal static class UserDefinedTypeHooks
    {
        private const int ExpectedNumSpells = 4;
        private const int ExpectedNumWeapons = 2;

        /// <summary>
        /// Verify input player data and produce no output.
        /// </summary>
        /// <param name="input">Input archive.</param>
        /// <param name="output">Output archive.</param>
        /// <returns>Result code.</returns>
        public static int InputPlayerNoOutput(RpcArchive input, RpcArchive output)
        {
            try
            {
                RpcArchive playerArchive = input.GetNamedArchiveValue("playerData");
                Player player = Player.FromRpcArchive(playerArchive);

                VerifyIncomingPlayerData(player);

                return 0;
            }
            catch (COMException error)
            {
                return error.HResult;
            }
        }

        /// <summary>
        /// Takes no input and outputs player data.
        /// </summary>
        /// <param name="input">Input archive.</param>
        /// <param name="output">Output archive.</param>
        /// <returns>Result code.</returns>
        public static int NoInputPlayerOutput(RpcArchive input, RpcArchive output)
        {
            try
            {
                Player player = InitializeOutgoingPlayer();
                player.ToRpcArchive(output);

                return 0;
            }
            catch (COMException error)
            {
                return error.HResult;
            }
        }

        /// <summary>
        /// Takes player data and outputs a serialized player.
        /// </summary>
        /// <param name="input">Input archive.</param>
        /// <param name="output">Output archive.</param>
        /// <returns>Result code.</returns>
        public static int InputPlayerOutputPlayer(RpcArchive input, RpcArchive output)
        {
            int result = InputPlayerNoOutput(input, output);

            if (result != 0)
            {
                return result;
            }

            result = NoInputPlayerOutput(input, output);

            if (result != 0)
            {
                return result;
            }

            return 0;
        }

        private static Player InitializeOutgoingPlayer()
        {
            Player player = new Player();

            player.Name = "Ragnor The Terrible";
            player.Health = 0.34245f;
            player.Spells = new string[] { "Fireball", "Freeze", "Hurricane", "Force" };

            player.Weapons = new Weapon[2];

            player.Weapons[0] = new Weapon();
            player.Weapons[0].Damage = 13454;
            player.Weapons[0].Modifier = -0.6;

            player.Weapons[1] = new Weapon();
            player.Weapons[1].Damage = 923;
            player.Weapons[1].Modifier = 2.4534;

            player.Stature = new Stature();
            player.Stature.Height = 82;
            player.Stature.Weight = 63;

            return player;
        }

        private static int VerifyIncomingPlayerData(Player playerData)
        {
            if (Math.Abs(playerData.Health - 76.375f) >= 0.0001)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Name != "Brian The Awesome")
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Spells.Length != ExpectedNumSpells)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Spells[0] != "Heal")
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Spells[1] != "Rage")
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Spells[2] != "Morph")
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Spells[3] != "Haste")
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Weapons.Length != ExpectedNumWeapons)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Weapons[0].Damage != 1411518807)
            {
                return RpcError.InvalidArg;
            }

            if (Math.Abs(playerData.Weapons[0].Modifier - 64848978978.8971) >= 0.0001)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Weapons[1].Damage != 576460752)
            {
                return RpcError.InvalidArg;
            }

            if (Math.Abs(playerData.Weapons[1].Modifier - -1796456897.68642) >= 0.0001)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Stature.Height != 73)
            {
                return RpcError.InvalidArg;
            }

            if (playerData.Stature.Weight != 189)
            {
                return RpcError.InvalidArg;
            }

            return 0;
        }
    }
}
